package com.googlecode.curvedmetromaps.core.logic.costfunction;

import com.googlecode.curvedmetromaps.core.model.MetroMap;
import com.googlecode.curvedmetromaps.core.model.MetroStation;

/**
 * Cost of orientation in drawing in comparison with geographic situation.
 */
public class GeographicOrientationCost extends BaseCost {

	public GeographicOrientationCost(MetroMap map) {
		super(map);
		createWeight("base", "Base weight", 20);
		createWeight("maxorientation", "Max orientation", 0.25 * Math.PI);
	}

	@Override
	public String getName() {
		return "Geographic orientation";
	}

	@Override
	public double calculateCost() {
		double result = 0;
		double maxOrientation = getWeight("maxorientation");

		for (MetroStation m1 : map.stations) {
			for (MetroStation m2 : map.stations) {
				if (m1.id != m2.id) {
					double dX = m2.x - m1.x;
					double dY = m2.y - m1.y;
					double dGeoX = m2.geoX - m1.geoX;
					double dGeoY = m2.geoY - m1.geoY;

					double angle = Math.atan2(dY, dX);
					double geoAngle = Math.atan2(dGeoY, dGeoX);

					// Calculate dAngle in the range [0,pi]
					double dAngle = angle - geoAngle;
					if (dAngle > Math.PI)
						dAngle = (2 * Math.PI) - dAngle;
					else if (dAngle <= -Math.PI)
						dAngle = (2 * Math.PI) + dAngle;
					else if (dAngle < 0)
						dAngle = -dAngle;

					if (dAngle > maxOrientation) {
						double cost = dAngle - maxOrientation;
						result += cost * cost;
					}
				}
			}
		}

		// Calculate average cost per vertex pair
		result /= map.stations.length;
		result += Float.MIN_VALUE;

		return getWeight("base") * result;
	}

}
